home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.6) '''This module implements facilities to deal with Debian changelogs.''' import re import unittest import warnings import debian_support class ChangelogParseError(StandardError): '''Indicates that the changelog could not be parsed''' is_user_error = True def __init__(self, line): self._line = line def __str__(self): return 'Could not parse changelog: ' + self._line class ChangelogCreateError(StandardError): '''Indicates that changelog could not be created, as all the information required was not given''' pass class VersionError(StandardError): '''Indicates that the version does not conform to the required format''' is_user_error = True def __init__(self, version): self._version = version def __str__(self): return 'Could not parse version: ' + self._version class Version(debian_support.Version, object): '''Represents a version of a Debian package.''' def __init__(self, version): version = str(version) debian_support.Version.__init__(self, version) self.full_version = version def __setattr__(self, attr, value): """Update all the attributes, given a particular piece of the version Allowable values for attr, hopefully self-explanatory: full_version epoch upstream_version debian_version Any attribute starting with __ is given to object's __setattr__ method. """ attrs = ('full_version', 'epoch', 'upstream_version', 'debian_version') if attr.startswith('_Version__'): object.__setattr__(self, attr, value) return None if attr not in attrs: raise AttributeError('Cannot assign to attribute ' + attr) attr not in attrs if attr == 'full_version': version = value p = re.compile('^(?:(?P<epoch>\\d+):)?' + '(?P<upstream_version>[A-Za-z0-9.+:~-]+?)' + '(?:-(?P<debian_version>[A-Za-z0-9.~+]+))?$') m = p.match(version) if m is None: raise VersionError(version) m is None for key, value in m.groupdict().items(): object.__setattr__(self, key, value) self._Version__asString = version else: d = { } for a in attrs[1:]: if a == attr: d[a] = value continue d[a] = getattr(self, a) version = '' if d['epoch'] and d['epoch'] != '0': version += d['epoch'] + ':' version += d['upstream_version'] if d['debian_version']: version += '-' + d['debian_version'] self.full_version = version full_version = property((lambda self: self._Version__asString)) class ChangeBlock(object): '''Holds all the information about one block from the changelog.''' def __init__(self, package = None, version = None, distributions = None, urgency = None, urgency_comment = None, changes = None, author = None, date = None, other_pairs = None): self._raw_version = None self._set_version(version) self.package = package self.distributions = distributions if not urgency: pass self.urgency = 'unknown' if not urgency_comment: pass self.urgency_comment = '' self._changes = changes self.author = author self.date = date self._trailing = [] if not other_pairs: pass self.other_pairs = { } self._no_trailer = False self._trailer_separator = ' ' def _set_version(self, version): if version is not None: self._raw_version = str(version) def _get_version(self): return Version(self._raw_version) version = property(_get_version, _set_version) def other_keys_normalised(self): norm_dict = { } for key, value in other_pairs.items(): key = key[0].upper() + key[1:].lower() m = xbcs_re.match(key) if m is None: key = 'XS-%s' % key norm_dict[key] = value return norm_dict def changes(self): return self._changes def add_trailing_line(self, line): self._trailing.append(line) def add_change(self, change): if self._changes is None: self._changes = [ change] else: changes = self._changes changes.reverse() added = False for i in range(len(changes)): m = blankline.match(changes[i]) if m is None: changes.insert(i, change) added = True break continue changes.reverse() if not added: changes.append(change) self._changes = changes def __str__(self): block = '' if self.package is None: raise ChangelogCreateError('Package not specified') self.package is None block += self.package + ' ' if self._raw_version is None: raise ChangelogCreateError('Version not specified') self._raw_version is None block += '(' + self._raw_version + ') ' if self.distributions is None: raise ChangelogCreateError('Distribution not specified') self.distributions is None block += self.distributions + '; ' if self.urgency is None: raise ChangelogCreateError('Urgency not specified') self.urgency is None block += 'urgency=' + self.urgency + self.urgency_comment for key, value in self.other_pairs.items(): block += ', %s=%s' % (key, value) block += '\n' if self.changes() is None: raise ChangelogCreateError('Changes not specified') self.changes() is None for change in self.changes(): block += change + '\n' if not self._no_trailer: if self.author is None: raise ChangelogCreateError('Author not specified') self.author is None if self.date is None: raise ChangelogCreateError('Date not specified') self.date is None block += ' -- ' + self.author + self._trailer_separator + self.date + '\n' for line in self._trailing: block += line + '\n' return block topline = re.compile('^(\\w%(name_chars)s*) \\(([^\\(\\) \\t]+)\\)((\\s+%(name_chars)s+)+)\\;' % { 'name_chars': '[-+0-9a-z.]' }, re.IGNORECASE) blankline = re.compile('^\\s*$') change = re.compile('^\\s\\s+.*$') endline = re.compile('^ -- (.*) <(.*)>( ?)((\\w+\\,\\s*)?\\d{1,2}\\s+\\w+\\s+\\d{4}\\s+\\d{1,2}:\\d\\d:\\d\\d\\s+[-+]\\d{4}(\\s+\\([^\\\\(\\)]\\))?\\s*)$') endline_nodetails = re.compile('^ --(?: (.*) <(.*)>( ?)((\\w+\\,\\s*)?\\d{1,2}\\s+\\w+\\s+\\d{4}\\s+\\d{1,2}:\\d\\d:\\d\\d\\s+[-+]\\d{4}(\\s+\\([^\\\\(\\)]\\))?))?\\s*$') keyvalue = re.compile('^([-0-9a-z]+)=\\s*(.*\\S)$', re.IGNORECASE) value_re = re.compile('^([-0-9a-z]+)((\\s+.*)?)$', re.IGNORECASE) xbcs_re = re.compile('^X[BCS]+-', re.IGNORECASE) emacs_variables = re.compile('^(;;\\s*)?Local variables:', re.IGNORECASE) vim_variables = re.compile('^vim:', re.IGNORECASE) cvs_keyword = re.compile('^\\$\\w+:.*\\$') comments = re.compile('^\\# ') more_comments = re.compile('^/\\*.*\\*/') old_format_re1 = re.compile('^(\\w+\\s+\\w+\\s+\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}\\s+[\\w\\s]*\\d{4})\\s+(.*)\\s+(<|\\()(.*)(\\)|>)') old_format_re2 = re.compile('^(\\w+\\s+\\w+\\s+\\d{1,2},?\\s*\\d{4})\\s+(.*)\\s+(<|\\()(.*)(\\)|>)') old_format_re3 = re.compile('^(\\w[-+0-9a-z.]*) \\(([^\\(\\) \t]+)\\)\\;?', re.IGNORECASE) old_format_re4 = re.compile('^([\\w.+-]+)(-| )(\\S+) Debian (\\S+)', re.IGNORECASE) old_format_re5 = re.compile('^Changes from version (.*) to (.*):', re.IGNORECASE) old_format_re6 = re.compile('^Changes for [\\w.+-]+-[\\w.+-]+:?\\s*$', re.IGNORECASE) old_format_re7 = re.compile('^Old Changelog:\\s*$', re.IGNORECASE) old_format_re8 = re.compile('^(?:\\d+:)?\\w[\\w.+~-]*:?\\s*$') class Changelog(object): '''Represents a debian/changelog file. You can ask it several things about the file. ''' def __init__(self, file = None, max_blocks = None, allow_empty_author = False, strict = True): '''Set up the Changelog for use. file is the contects of the changelog. ''' self._blocks = [] self.initial_blank_lines = [] if file is not None: try: self.parse_changelog(file, max_blocks = max_blocks, allow_empty_author = allow_empty_author, strict = strict) except ChangelogParseError: pass except: None<EXCEPTION MATCH>ChangelogParseError None<EXCEPTION MATCH>ChangelogParseError def _parse_error(self, message, strict): if strict: raise ChangelogParseError(message) strict warnings.warn(message) def parse_changelog(self, file, max_blocks = None, allow_empty_author = False, strict = True): first_heading = 'first heading' next_heading_or_eof = 'next heading of EOF' start_of_change_data = 'start of change data' more_changes_or_trailer = 'more change data or trailer' slurp_to_end = 'slurp to end' self._blocks = [] self.initial_blank_lines = [] current_block = ChangeBlock() changes = [] state = first_heading old_state = None if isinstance(file, basestring): if file[-1] != '\n': file += '\n' file = file.split('\n')[:-1] for line in file: if state == first_heading or state == next_heading_or_eof: top_match = topline.match(line) blank_match = blankline.match(line) if top_match is not None: if max_blocks is not None and len(self._blocks) >= max_blocks: return None current_block.package = top_match.group(1) current_block._raw_version = top_match.group(2) current_block.distributions = top_match.group(3).lstrip() pairs = line.split(';', 1)[1] all_keys = { } other_pairs = { } for pair in pairs.split(','): pair = pair.strip() kv_match = keyvalue.match(pair) if kv_match is None: self._parse_error("Invalid key-value pair after ';': %s" % pair, strict) continue key = kv_match.group(1) value = kv_match.group(2) if key.lower() in all_keys: self._parse_error('Repeated key-value: %s' % key.lower(), strict) all_keys[key.lower()] = value if key.lower() == 'urgency': val_match = value_re.match(value) None if val_match is None else comment is not None other_pairs[key] = value current_block.other_pairs = other_pairs state = start_of_change_data elif blank_match is not None: if state == first_heading: self.initial_blank_lines.append(line) else: self._blocks[-1].add_trailing_line(line) else: emacs_match = emacs_variables.match(line) vim_match = vim_variables.match(line) cvs_match = cvs_keyword.match(line) comments_match = comments.match(line) more_comments_match = more_comments.match(line) if (emacs_match is not None or vim_match is not None) and state != first_heading: self._blocks[-1].add_trailing_line(line) old_state = state state = slurp_to_end continue if cvs_match is not None and comments_match is not None or more_comments_match is not None: if state == first_heading: self.initial_blank_lines.append(line) continue self._blocks[-1].add_trailing_line(line) continue if (old_format_re1.match(line) is not None and old_format_re2.match(line) is not None and old_format_re3.match(line) is not None and old_format_re4.match(line) is not None and old_format_re5.match(line) is not None and old_format_re6.match(line) is not None and old_format_re7.match(line) is not None or old_format_re8.match(line) is not None) and state != first_heading: self._blocks[-1].add_trailing_line(line) old_state = state state = slurp_to_end continue self._parse_error('Unexpected line while looking for %s: %s' % (state, line), strict) if state == first_heading: self.initial_blank_lines.append(line) else: self._blocks[-1].add_trailing_line(line) state == first_heading if state == start_of_change_data or state == more_changes_or_trailer: change_match = change.match(line) end_match = endline.match(line) end_no_details_match = endline_nodetails.match(line) blank_match = blankline.match(line) if change_match is not None: changes.append(line) state = more_changes_or_trailer elif end_match is not None: if end_match.group(3) != ' ': self._parse_error('Badly formatted trailer line: %s' % line, strict) current_block._trailer_separator = end_match.group(3) current_block.author = '%s <%s>' % (end_match.group(1), end_match.group(2)) current_block.date = end_match.group(4) current_block._changes = changes self._blocks.append(current_block) changes = [] current_block = ChangeBlock() state = next_heading_or_eof elif end_no_details_match is not None: if not allow_empty_author: self._parse_error('Badly formatted trailer line: %s' % line, strict) continue current_block._changes = changes self._blocks.append(current_block) changes = [] current_block = ChangeBlock() state = next_heading_or_eof elif blank_match is not None: changes.append(line) else: cvs_match = cvs_keyword.match(line) comments_match = comments.match(line) more_comments_match = more_comments.match(line) if cvs_match is not None and comments_match is not None or more_comments_match is not None: changes.append(line) continue self._parse_error('Unexpected line while looking for %s: %s' % (state, line), strict) changes.append(line) change_match is not None if state == slurp_to_end: if old_state == next_heading_or_eof: self._blocks[-1].add_trailing_line(line) else: changes.append(line) old_state == next_heading_or_eof if not False: raise AssertionError, 'Unknown state: %s' % state if (state != next_heading_or_eof or state != slurp_to_end or state == slurp_to_end) and old_state != next_heading_or_eof: self._parse_error('Found eof where expected %s' % state, strict) current_block._changes = changes current_block._no_trailer = True self._blocks.append(current_block) def get_version(self): '''Return a Version object for the last version''' return self._blocks[0].version def set_version(self, version): '''Set the version of the last changelog block version can be a full version string, or a Version object ''' self._blocks[0].version = Version(version) version = property(get_version, set_version, doc = 'Version object for last changelog block') full_version = property((lambda self: self.version.full_version)) epoch = property((lambda self: self.version.epoch)) debian_version = property((lambda self: self.version.debian_version)) upstream_version = property((lambda self: self.version.upstream_version)) def get_package(self): '''Returns the name of the package in the last version.''' return self._blocks[0].package def set_package(self, package): self._blocks[0].package = package package = property(get_package, set_package, doc = 'Name of the package in the last version') def get_versions(self): '''Returns a list of version objects that the package went through.''' return [ block.version for block in self._blocks ] versions = property(get_versions, doc = 'List of version objects the package went through') def _raw_versions(self): return [ block._raw_version for block in self._blocks ] def __str__(self): cl = '\n'.join(self.initial_blank_lines) for block in self._blocks: cl += str(block) return cl def set_distributions(self, distributions): self._blocks[0].distributions = distributions distributions = property((lambda self: self._blocks[0].distributions), set_distributions) def set_urgency(self, urgency): self._blocks[0].urgency = urgency urgency = property((lambda self: self._blocks[0].urgency), set_urgency) def add_change(self, change): self._blocks[0].add_change(change) def set_author(self, author): self._blocks[0].author = author author = property((lambda self: self._blocks[0].author), set_author) def set_date(self, date): self._blocks[0].date = date date = property((lambda self: self._blocks[0].date), set_date) def new_block(self, **kwargs): block = ChangeBlock(**kwargs) block.add_trailing_line('') self._blocks.insert(0, block) def write_to_open_file(self, file): file.write(self.__str__()) def _test(): import doctest doctest.testmod() unittest.main() class ChangelogTests(unittest.TestCase): def test_create_changelog(self): c = open('test_changelog').read() cl = Changelog(c) cs = str(cl) clines = c.split('\n') cslines = cs.split('\n') for i in range(len(clines)): self.assertEqual(clines[i], cslines[i]) self.assertEqual(len(clines), len(cslines), 'Different lengths') def test_create_changelog_single_block(self): c = open('test_changelog').read() cl = Changelog(c, max_blocks = 1) cs = str(cl) self.assertEqual(cs, 'gnutls13 (1:1.4.1-1) unstable; urgency=HIGH\n\n [ James Westby ]\n * New upstream release.\n * Remove the following patches as they are now included upstream:\n - 10_certtoolmanpage.diff\n - 15_fixcompilewarning.diff\n - 30_man_hyphen_*.patch\n * Link the API reference in /usr/share/gtk-doc/html as gnutls rather than\n gnutls-api so that devhelp can find it.\n\n -- Andreas Metzler <ametzler@debian.org> Sat, 15 Jul 2006 11:11:08 +0200\n\n') def test_modify_changelog(self): c = open('test_modify_changelog1').read() cl = Changelog(c) cl.package = 'gnutls14' cl.version = '1:1.4.1-2' cl.distributions = 'experimental' cl.urgency = 'medium' cl.add_change(' * Add magic foo') cl.author = 'James Westby <jw+debian@jameswestby.net>' cl.date = 'Sat, 16 Jul 2008 11:11:08 -0200' c = open('test_modify_changelog2').read() clines = c.split('\n') cslines = str(cl).split('\n') for i in range(len(clines)): self.assertEqual(clines[i], cslines[i]) self.assertEqual(len(clines), len(cslines), 'Different lengths') def test_add_changelog_section(self): c = open('test_modify_changelog2').read() cl = Changelog(c) cl.new_block(package = 'gnutls14', version = Version('1:1.4.1-3'), distributions = 'experimental', urgency = 'low', author = 'James Westby <jw+debian@jameswestby.net>') self.assertRaises(ChangelogCreateError, cl.__str__) cl.set_date('Sat, 16 Jul 2008 11:11:08 +0200') cl.add_change('') cl.add_change(' * Foo did not work, let us try bar') cl.add_change('') c = open('test_modify_changelog3').read() clines = c.split('\n') cslines = str(cl).split('\n') for i in range(len(clines)): self.assertEqual(clines[i], cslines[i]) self.assertEqual(len(clines), len(cslines), 'Different lengths') def test_strange_changelogs(self): ''' Just opens and parses a strange changelog ''' c = open('test_strange_changelog').read() cl = Changelog(c) def test_set_version_with_string(self): c1 = Changelog(open('test_modify_changelog1').read()) c2 = Changelog(open('test_modify_changelog1').read()) c1.version = '1:2.3.5-2' c2.version = Version('1:2.3.5-2') self.assertEqual(c1.version, c2.version) self.assertEqual((c1.full_version, c1.epoch, c1.upstream_version, c1.debian_version), (c2.full_version, c2.epoch, c2.upstream_version, c2.debian_version)) def test_changelog_no_author(self): cl_no_author = 'gnutls13 (1:1.4.1-1) unstable; urgency=low\n\n * New upstream release.\n\n --\n' c1 = Changelog() c1.parse_changelog(cl_no_author, allow_empty_author = True) self.assertEqual(c1.author, None) self.assertEqual(c1.date, None) self.assertEqual(c1.package, 'gnutls13') c2 = Changelog() self.assertRaises(ChangelogParseError, c2.parse_changelog, cl_no_author) def test_magic_version_properties(self): c = Changelog(open('test_changelog')) self.assertEqual(c.debian_version, '1') self.assertEqual(c.full_version, '1:1.4.1-1') self.assertEqual(c.upstream_version, '1.4.1') self.assertEqual(c.epoch, '1') self.assertEqual(str(c.version), c.full_version) def test_allow_full_stops_in_distribution(self): c = Changelog(open('test_changelog_full_stops')) self.assertEqual(c.debian_version, None) self.assertEqual(c.full_version, '1.2.3') self.assertEqual(str(c.version), c.full_version) class VersionTests(unittest.TestCase): def _test_version(self, full_version, epoch, upstream, debian): v = Version(full_version) self.assertEqual(v.full_version, full_version, 'Full version broken') self.assertEqual(v.epoch, epoch, 'Epoch broken') self.assertEqual(v.upstream_version, upstream, 'Upstram broken') self.assertEqual(v.debian_version, debian, 'Debian broken') def testversions(self): self._test_version('1:1.4.1-1', '1', '1.4.1', '1') self._test_version('7.1.ds-1', None, '7.1.ds', '1') self._test_version('10.11.1.3-2', None, '10.11.1.3', '2') self._test_version('4.0.1.3.dfsg.1-2', None, '4.0.1.3.dfsg.1', '2') self._test_version('0.4.23debian1', None, '0.4.23debian1', None) self._test_version('1.2.10+cvs20060429-1', None, '1.2.10+cvs20060429', '1') self._test_version('0.2.0-1+b1', None, '0.2.0', '1+b1') self._test_version('4.3.90.1svn-r21976-1', None, '4.3.90.1svn-r21976', '1') self._test_version('1.5+E-14', None, '1.5+E', '14') self._test_version('20060611-0.0', None, '20060611', '0.0') self._test_version('0.52.2-5.1', None, '0.52.2', '5.1') self._test_version('7.0-035+1', None, '7.0', '035+1') self._test_version('1.1.0+cvs20060620-1+2.6.15-8', None, '1.1.0+cvs20060620-1+2.6.15', '8') self._test_version('1.1.0+cvs20060620-1+1.0', None, '1.1.0+cvs20060620', '1+1.0') self._test_version('4.2.0a+stable-2sarge1', None, '4.2.0a+stable', '2sarge1') self._test_version('1.8RC4b', None, '1.8RC4b', None) self._test_version('0.9~rc1-1', None, '0.9~rc1', '1') self._test_version('2:1.0.4+svn26-1ubuntu1', '2', '1.0.4+svn26', '1ubuntu1') self._test_version('2:1.0.4~rc2-1', '2', '1.0.4~rc2', '1') def test_version_updating(self): v = Version('1:1.4.1-1') v.debian_version = '2' self.assertEqual(v.debian_version, '2') self.assertEqual(v.full_version, '1:1.4.1-2') v.upstream_version = '1.4.2' self.assertEqual(v.upstream_version, '1.4.2') self.assertEqual(v.full_version, '1:1.4.2-2') v.epoch = '2' self.assertEqual(v.epoch, '2') self.assertEqual(v.full_version, '2:1.4.2-2') self.assertEqual(str(v), v.full_version) v.full_version = '1:1.4.1-1' self.assertEqual(v.full_version, '1:1.4.1-1') self.assertEqual(v.epoch, '1') self.assertEqual(v.upstream_version, '1.4.1') self.assertEqual(v.debian_version, '1') if __name__ == '__main__': _test()